package com.beeasy.hzbpm.work;


import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.github.llyb120.nami.core.Async;
import com.github.llyb120.nami.core.Config;
import com.github.llyb120.json.Arr;
import com.github.llyb120.json.Json;
import com.github.llyb120.json.Obj;
import com.mongodb.Block;
import com.mongodb.client.MongoCollection;
import org.bson.Document;

import java.util.*;
import java.util.stream.Collectors;

import static com.beeasy.hzbpm.bean.MongoService.db;
import static com.github.llyb120.json.Json.*;

public class FullMode implements WorkMode {

    @Override
    public boolean isSu(String uid) {
        return db.getCollection("user").count(o(
            "su", 1,
            "id", uid
        ).toBson()) > 0;
    }

    @Override
    public String getUserCacheName(String uid) {
        Document item = db.getCollection("user").find(o(
            "id", uid
        ).toBson())
            .projection(o("true_name", 1).toBson())
            .first();
        return Optional.ofNullable(item)
            .map(e -> e.getString("true_name"))
            .orElse(null);
    }

    @Override
    public Obj getUserNamesMap(Collection<String> uids) {
        Obj ret = o();
        if (uids == null || uids.isEmpty()) {
            return ret;
        }
        db.getCollection("user").find(o("id", o("$in", uids)).toBson())
            .projection(o("true_name", 1, "id", 1).toBson())
            .forEach((Block<? super Document>) e -> {
                ret.put(e.getString("id"), e.getString("true_name"));
            });
        return ret;
    }

    @Override
    public List<Obj> getRoleList() {
        return db.getCollection("role")
            .aggregate(a(
                o("$project", o(
                    "_id", 0,
                    "id", 1,
                    "text", "$name",
                    "parent_id", "$pid"
                )),
                o("$sort", o(
                    "sort", 1
                ))
            ).toBson())
            .into(a())
            .oa();
    }

    @Override
    public List<Obj> getDepList() {
        return db.getCollection("dep")
            .aggregate(a(
                o("$project", o(
                    "_id", 0,
                    "id", 1,
                    "text", "$name",
                    "parent_id", "$pid",
                        "sort",1
                )),
                o("$sort", o(
                    "sort", 1
                ))
            ).toBson())
            .into(a())
            .oa();
    }

    @Override
    public void addLog(String uid, String msg) {
        Async.execute(() -> {
            String url = Config.config.var.ss("push-log-url");
            Date date = new Date();
            HttpUtil.createPost(url)
                .header("Content-Type", "application/json; charset=utf-8")
                .body(o(
                    "uid", uid,
                    "content", msg,
                    "timestamp", date
                ).toString())
                .timeout(30000)
                .executeAsync();

            /*Obj params = o(
                    "uid", uid,
                    "content", msg,
                    "timestamp", System.currentTimeMillis()
            );
            String rep = HttpUtil.get(url, o(
                    "param", params
            ));
            Obj info = Json.parse(rep);*/

            Document ret;
            db.getCollection("log")
                .insertOne(ret = o(
//                "id,add_time,controller,method,user_id"
                    "id", IdUtil.objectId(),
                    "add_time", date,
                    "type", "bpm",
                    "content", msg,
                    "uid", uid
                ).toBson());
        });
    }


    @Override
    public List<String> getPhones(Collection<String> uids) {
        if (uids == null || uids.isEmpty()) {
            return a();
        }
        return (db.getCollection("user")
            .find(o("id", o("$in", uids)).toBson())
            .projection(o("phone", 1).toBson())
            .into(a())
            .stream()
            .map(e -> ooo(e).s("phone", ""))
            .distinct()
            .collect(Collectors.toList()));
    }


//    @Override
//    @Deprecated
//    public List<Obj> getOrgList(Collection<String> ids) {
//        if (ids == null || ids.isEmpty()) {
//            return a();
//        }
//        return (db.getCollection("org")
//            .find(o("id", o("$in", ids)).toBson())
//            .projection(o(
//                "id", 1,
//                "_id", 0,
//                "name", 1
//            ).toBson())
//            .into(a())
//            .stream()
//            .map(Json::ooo)
//            .collect(Collectors.toList()));
//    }

    @Override
    public List<Obj> getDepList(Collection<String> ids) {
        if (ids == null || ids.isEmpty()) {
            return a();
        }
        return (db.getCollection("dep")
            .find(o("id", o("$in", ids)).toBson())
            .projection(o(
                "id", 1,
                "_id", 0,
                "name", 1
            ).toBson())
            .into(a())
            .stream()
            .map(Json::ooo)
            .collect(Collectors.toList()));
    }

    @Override
    public List<Obj> getRoleList(Collection<String> ids) {
        if (ids == null || ids.isEmpty()) {
            return a();
        }
        return (db.getCollection("role")
            .find(o("id", o("$in", ids)).toBson())
            .projection(o(
                "id", 1,
                "_id", 0,
                "name", 1
            ).toBson())
            .into(a())
            .stream()
            .map(Json::ooo)
            .collect(Collectors.toList()));
    }

    @Override
    public List<Obj> getUserNameList(Collection<String> ids) {
        if (ids == null || ids.isEmpty()) {
            return a();
        }
        return (db.getCollection("user")
            .find(o("id", a("$in", ids)).toBson())
            .projection(o(
                "id", 1,
                "_id", 0,
                "true_name", 1
            ).toBson())
            .into(a())
            .stream()
            .map(Json::ooo)
            .collect(Collectors.toList()));
    }

    @Override
    public List<String> getUserRoleIds(String uid) {
        Document doc = db.getCollection("user")
            .find(o("id", uid).toBson())
            .first();
        if (doc == null) {
            return a();
        }
        Obj user = ooo(doc);
        return (user.aa("orgs")
            .stream()
            .filter(e -> ooo(e).s("type", "").equals("ROLE"))
            .map(e -> ooo(e).s("id"))
            .distinct()
            .collect(Collectors.toList()));
    }

    public List<String> getRoleUserIds(Collection<String> oid) {
        if(null == oid || oid.isEmpty()){
            return a();
        }
        return db.getCollection("user")
            .find(o(
                "orgs", o(
                    "$elemMatch", o(
                        "id", o("$in", oid),
                        "type", "ROLE"
                    )
                )
            ).toBson())
            .projection(o("id",1,"_id", 0).toBson())
            .into(a())
            .stream()
            .map(e -> e.getString("id"))
            .distinct()
            .collect(Collectors.toList());
    }

    @Override
    public List<Obj> getDepUsersNew(String did) {
        return db.getCollection("user")
            .aggregate(
                a(
                    o("$match", o(
                        "orgs", o(
                            "$elemMatch", o(
                                "type", "DEP",
                                "id", did
                            )
                        )
                    )),
                    o("$project", o(
                        "_id", 0,
                        "uid", "$id",
                        "utname", "$true_name",
                        "uname", "$username",
                        "type", "USER"
                    ))
                ).toBson()
            ).into(a())
            .oa();
    }

    @Override
    public List<Obj> getRoleUsersNew(String did) {
        return db.getCollection("user")
            .aggregate(
                a(
                    o("$match", o(
                        "orgs", o(
                            "$elemMatch", o(
                                "$and", a(
                                    o("type", "ROLE"),
                                    o("id", did)
                                )
                            )
                        )
                    )),
                    o("$project", o(
                        "_id", 0,
                        "uid", "$id",
                        "utname", "$true_name",
                        "uname", "$username",
                        "otype", "ROLE"
                    ))
                ).toBson()
            ).into(a())
            .oa();
    }

    @Override
    public List<String> getManagersFromDeps(Collection<String> dids) {
        if (dids == null || dids.isEmpty()) {
            return a();
        }
        return db.getCollection("user")
            .aggregate(
                a(
                    o("$match", o(
                        "orgs", o(
                            "$elemMatch", o(
                                    "type", "MANAGER",
                                    "id", o("$in", dids)
                            )
                        )
                    )),
                    o("$project", o(
                        "_id", 0,
                        "id",1
                    ))
                ).toBson()
            ).into(a())
            .stream()
            .map(e -> e.getString("id"))
            .distinct()
            .collect(Collectors.toList());
    }

    @Override
    public List<Obj> getNextNodeDealers(SmartChooseOption option) {
        MongoCollection<Document> usercol = db.getCollection("user");
        //第一步，生效过滤规则
        //如果选择了本部门或平行部门
        Obj user = ooo(usercol.find(o("id", option.currentUserId).toBson()).first());
        List<Obj> orgs = user.aa("orgs").oa();
        List<Obj> uids = a();
        // 选人过滤规则集合
        List<Obj> chooseUids = a();
        List<String> dids = orgs.stream()
            .filter(e -> e.ss("type").equals("DEP"))
            .map(e -> e.ss("id"))
            .collect(Collectors.toList());
        if(option.chooseSelfDep){
            //查询同级部门的人
            chooseUids.addAll(usercol
                    .aggregate(a(
                            o("$match", o(
                                    "orgs", o(
                                            "$elemMatch", o(
                                                            "type", "DEP",
                                                            "id", o("$in", dids)
                                            )
                                    )
                            )),
                            o(
                                    "$project", o(
                                            "uid", "$id",
                                            "_id", 0,
                                            "utname", "$true_name"
                                    )
                            )
                    ).toBson()).into(a()).oa());

                /*.find(o(
                    "orgs", o(
                        "$elemMatch", o(
                            "$and", a(
                                o("type", "USER"),
                                o("oid", o("$in", dids))
                            )
                        )
                    )
                ).toBson())
                .projection(o("id", 1, "_id", 0, "true_name",1).toBson())
                .into(a())
                .oa());*/
        }
        // 是否选择平行部门经办人
        if(option.chooseParallelDep){

            List<Obj> praentId1 = usercol.aggregate(a(
                    o("$match", o("id", option.currentUserId)),
                    o("$lookup", o(
                            "from", "dep",
                            "localField", "orgs.id",
                            "foreignField", "id",
                            "as", "dep"
                            )
                    ),
                    o(
                            "$project", o(
                                    "dep.pid", 1,
                                    "_id", 0
                            )
                    )
            ).toBson()).into(a()).oa();
            List<String> pidList = new ArrayList<>();
            for (Obj obj : praentId1) {
                Arr dep = obj.a("dep");
                if(null == dep){
                    continue;
                }
                for (Object d : dep) {
                    Document a = (Document)d;
                    String pid = (String) a.get("pid");
                    if(null == pid){
                        continue;
                    }
                    pidList.add(pid);
                }
            }

            MongoCollection<Document> depCol = db.getCollection("dep");
            List<Obj> ids = depCol
                    .find(
                            o("pid", o("$in", pidList))
                                    .toBson())
                    .projection(o("id", 1, "_id", 0).toBson())
                    .into(a())
                    .oa();

            List<String> idList = new ArrayList<>();
            for (Obj id : ids) {
                idList.add(id.s("id"));
            }
            chooseUids.addAll(usercol
                    .aggregate(a(
                            o("$match", o("orgs.id", o("$in", idList))),
                            o(
                                    "$project", o(
                                            "uid", "$id",
                                            "_id", 0,
                                            "utname", "$true_name"
                                    )
                            )
                    ).toBson()).into(a()).oa());
                    /*.find(
                            o("orgs.id", o("$in", idList))
                                    .toBson())
                    .projection(o("id", 1, "_id", 0, "true_name",1).toBson())
                    .into(a())
                    .oa());*/

//            pdids = orgcol.find(o(
//                "id", o("$in", )
//            ))
        }


        List<String> didLists = orgs.stream()
                .filter(e -> e.ss("type").equals("DEP"))
                .map(e -> e.ss("id"))
                .collect(Collectors.toList());

        // 自动选人集合
        List<Obj> autoUids = a();

        // 是否自动选择流程发起人
        if(option.choosePublisher){
            Obj currUid = o();
            currUid.put("uid",option.pubUserId);
            currUid.put("utname",getUserCacheName(option.pubUserId));
            autoUids.add(currUid);
        }
        if(option.autoChooseSelfManager){
            autoUids.addAll(this.getManager(didLists, "MANAGER"));
        }
        if(option.autoChooseTopManager0){
            // 上级主管领导
            List<Obj> top_manager0 = this.getManager(didLists, "TOP_MANAGER0");
            //上级部门的主管领导
            //上级部门
            List<String> topDid = db.getCollection("dep").find(
                            o("id", o("$in", didLists))
            .toBson())
                    .projection(o("pid", 1, "_id", 0).toBson())
                    .into(a())
                    .oa()
                    .stream()
                    .map(e -> e.ss("pid"))
                    .collect(Collectors.toList());;
            //主管领导
            List<Obj> manager = this.getManager(topDid, "MANAGER");
            autoUids.addAll(manager);
            autoUids.addAll(top_manager0);
        }

        if(option.autoChooseTopManager1){
            autoUids.addAll(this.getManager(didLists, "TOP_MANAGER1"));
        }






        // 通过一坨角色/部门查询uid
        List<Obj> orList = getORList(option.baseDepIds);
        orList.addAll(getORList(option.baseRoleIds));
        Obj userObj = getUserNamesMap(option.baseUserIds);
        for(Map.Entry<String, Object> entry : userObj.entrySet()){
            Obj users = o();
            users.put("uid", entry.getKey());
            users.put("utname",entry.getValue());
            orList.add(users);
        }

        if(option.chooseAll && option.autoNot){
            autoUids.addAll(orList);
        }

        if(chooseUids.isEmpty()){
            uids = autoUids;
        }else if(autoUids.isEmpty()){
            uids = chooseUids;
        }else{
            for(Obj choose : chooseUids){
                for(Obj auto : autoUids){
                    if(StrUtil.equals(choose.s("uid"), auto.s("uid"))){
                        Obj o = o();
                        o.put("uid", choose.s("uid"));
                        o.put("utname", choose.s("utname"));
                        uids.add(o);
                    }
                }
            }
        }

        List<String> chooseList = new ArrayList<>();
        for(Obj list : orList){
            chooseList.add(list.s("uid"));
        }

        if(option.autoChooseUser){
            List<Obj> choose = db.getCollection("user").aggregate(a(
                    o("$match", o("id", o("$in", option.chooseList))),
                    o(
                            "$project", o(
                                    "uid", "$id",
                                    "_id", 0,
                                    "utname", "$true_name"
                            )
                    )
            ).toBson()).into(a()).oa();
            uids = choose;
        }



        List<Obj> resp = a();
        if(!uids.isEmpty()){
            for (Obj uid : uids) {
                for(Obj or : orList){
                    if(StrUtil.equals(uid.s("uid"), or.s("uid"))){
                        Obj o = o();
                        o.put("uid", uid.s("uid"));
                        o.put("utname", uid.s("utname"));
                        resp.add(o);
                    }
                }
            }

//            uids.retainAll(orList);

        }
//        else{
//            resp = orList;
//        }


        /*List<Obj> resp = a();
        if(!uids.isEmpty()){
            for (Obj uid : uids) {
                Obj o = o();
                o.put("uid", uid.s("id"));
                o.put("utname", getUserCacheName(uid.s("id")));
                resp.add(o);
            }
        }*/


        return resp;
    }

    public List<Obj> getORList(Collection<String> ids) {
        if (ids == null || ids.isEmpty()) {
            return a();
        }


        return db.getCollection("user").aggregate(a(
                o("$match", o("orgs.id", o("$in", ids))),
                o(
                        "$project", o(
                                "uid", "$id",
                                "_id", 0,
                                "utname", "$true_name"
                        )
                )
        ).toBson()).into(a()).oa();

    }

    /**
     * 自动选择本部门主管/自动选择上级主管领导/自动选择上级分管领导
     * @param type 本部门主管为MANAGER 上级分管领导为TOP_MANAGER0 上级分管领导为TOP_MANAGER1
     * @return
     */
    private List<Obj> getManager(List<String> dids, String type){
        MongoCollection<Document> userCol = db.getCollection("user");
        List<Obj> uidList = userCol
                .aggregate(a(
                        o("$match", o(
                                "$and", a(
                                        o("orgs.type", type),
                                        o("orgs.id", o("$in", dids))
                                ))),
                        o(
                                "$project", o(
                                        "uid", "$id",
                                        "_id", 0,
                                        "utname", "$true_name"
                                )
                        )
                ).toBson()).into(a()).oa();
                /*.find(o(
                        "$and", a(
                                o("orgs.type", type),
                                o("orgs.id", o("$in", dids))
                        )
                ).toBson())
                .projection(o("id", 1, "_id", 0, "true_name",1).toBson())
                .into(a())
                .oa();*/
        return uidList;
    }

    /**
     * 自动选择上级主管领导
     * @return
     */
    private List<Obj> topManager0(List<String> dids){


        return null;
    }

    @Override
    public List<Obj> searchUserByKeyword(String keyword) {
        return db.getCollection("user")
            .aggregate(a(
                o("$match",o(
                    "$or", a(
                        o("username", o("$regex", keyword)),
                        o("true_name", o("$regex", keyword))
                    )
                ))    ,
                o("$project", o(
                    "_id",0,
                    "uid", "$id",
                    "utname", "$true_name"
                )),
                o("$skip", 0),
                o("$limit", 30)
            ).toBson())
            .into(a())
            .stream()
            .map(e -> ooo(e))
            .collect(Collectors.toList());
    }
}
